package io.github.wslxm.springbootplus2.starter.swagger.sort;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.stereotype.Component;
import springfox.documentation.spring.web.plugins.Docket;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.Set;

/**
 * 根据
 * @author 王松
 * @mail 1720696548@qq.com
 * @date 2024/8/23 15:26
 */
@Component
public class ApiModelProcessor implements BeanPostProcessor {

    @Autowired
    private ApplicationContext context;

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        // 这里可以检查 bean 是否是 Docket 类型，如果是则进行处理
        if (bean instanceof Docket) {
            processApiModels();
        }
        return bean;
    }

    private void processApiModels() {
        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
        scanner.addIncludeFilter(new AnnotationTypeFilter(ApiModel.class));
        // 获取当前项目需要扫描的包路径
        String[] packages = this.getPackages();
        for (String aPackage : packages) {
            Set<BeanDefinition> components = scanner.findCandidateComponents(aPackage);
            try {
                for (BeanDefinition component : components) {
                    Class<?> clazz = Class.forName(component.getBeanClassName());
                    Field[] fields = clazz.getDeclaredFields();
                    for (int i = 0; i < fields.length; i++) {
                        ApiModelProperty apiModelProperty = fields[i].getAnnotation(ApiModelProperty.class);
                        if (apiModelProperty != null) {
                            // 修改注解的 position 属性值
                            this.changeAnnotationValue(apiModelProperty, "position", i + 1);
                        }
                    }
                }
            } catch (NoSuchFieldException | IllegalAccessException | ClassNotFoundException e) {
                throw new RuntimeException(e);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    // 反射修改注解属性的方法
    private void changeAnnotationValue(Annotation annotation, String key, Object newValue) throws Exception {
        // 获取注解的代理实例
        InvocationHandler handler = Proxy.getInvocationHandler(annotation);
        // 获取 AnnotationInvocationHandler 的 memberValues 字段
        Field memberValuesField = handler.getClass().getDeclaredField("memberValues");
        memberValuesField.setAccessible(true);
        // 获取 memberValues
        Map<String, Object> memberValues = (Map<String, Object>) memberValuesField.get(handler);
        // 修改属性值
        memberValues.put(key, newValue);
    }

    /**
     * 获取启动类注解上需要扫描的路径
     *
     * @return {@link String[]}
     */
    private String[] getPackages() {
        // 获取启动类注解上需要扫描的路径
        Map<String, Object> annotatedBeans = context.getBeansWithAnnotation(SpringBootApplication.class);
        Class<?> aClass = annotatedBeans.values().toArray()[0].getClass();
        SpringBootApplication annotation = aClass.getAnnotation(SpringBootApplication.class);
        return annotation.scanBasePackages();
    }
}